{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Re-executing a Workflow\n",
    "\n",
    "Use the Covalent `redispatch` command to re-execute a workflow with:\n",
    "\n",
    "* New input parameters\n",
    "* Updated or replacement task definitions\n",
    "\n",
    "Optionally, you can reuse previous results as well.\n",
    "\n",
    "### Prerequisites\n",
    "\n",
    "Run the Covalent server."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Procedure\n",
    "\n",
    "Construct a workflow."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "import covalent as ct\n",
    "\n",
    "@ct.electron\n",
    "def task_0(a):\n",
    "    return a\n",
    "\n",
    "\n",
    "@ct.electron\n",
    "def task_1(a, b):\n",
    "    return a + b\n",
    "\n",
    "\n",
    "@ct.electron\n",
    "def task_2(a, b):\n",
    "    return a / b\n",
    "\n",
    "# For demonstrating redispatch\n",
    "@ct.electron\n",
    "def task_2_redefined(a, b):\n",
    "    return a * b\n",
    "\n",
    "\n",
    "@ct.lattice\n",
    "def workflow(a, b):\n",
    "    res_0 = task_0(a)\n",
    "    res_1 = task_1(res_0, b)\n",
    "    res_2 = task_2(res_1, b)\n",
    "    return res_2\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2. Dispatch the workflow."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dispatch id: f6553e74-86dc-4ed7-9f38-6c3c71b6ebb7\n",
      "Workflow execution status: COMPLETED\n",
      "Workflow output: 1.5\n"
     ]
    }
   ],
   "source": [
    "dispatch_id = ct.dispatch(workflow)(1, 2) # Input parameters are a=1, b=2\n",
    "print(f\"Dispatch id: {dispatch_id}\")\n",
    "result = ct.get_result(dispatch_id, wait=True)\n",
    "print(f\"Workflow execution status: {result.status}\")\n",
    "print(f\"Workflow output: {result.result}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3. Use the Covalent `redispatch` function to redispatch the workflow, replacing one or more of the tasks. (One task is replaced in these demos. In practice you can re-execute any number of tasks.) \n",
    "\n",
    "Three cases are illustrated below:\n",
    "\n",
    "    1. Using the previous input parameters.\n",
    "    2. Reusing (where possible) previous task results.\n",
    "    3. Using new input parameters.\n",
    "    \n",
    "#### 1. Using the previous input parameters\n",
    "\n",
    "Leave the input parameters empty to dispatch the workflow with the previous input parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Redispatch id: 3c3be52a-4ee7-4b17-9732-c12334530839\n",
      "Redispatched workflow execution status: COMPLETED\n",
      "Redispatched workflow output: 6\n"
     ]
    }
   ],
   "source": [
    "redispatch_id = ct.redispatch(\n",
    "    dispatch_id, \n",
    "    replace_electrons={\"task_2\": task_2_redefined}\n",
    ")()\n",
    "print(f\"Redispatch id: {redispatch_id}\")\n",
    "result = ct.get_result(redispatch_id, wait=True)\n",
    "print(f\"Redispatched workflow execution status: {result.status}\")\n",
    "print(f\"Redispatched workflow output: {result.result}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2: Reusing the previous task results\n",
    "\n",
    "Set the keyword argument `reuse_previous_results` to `True` to reuse computed results wherever possible. The`reuse_previous_results` flag defaults to `False`, so you must explicitly set it to `True` to reuse results.\n",
    "\n",
    "\n",
    "Warning: When the workflow involves stochastic results (values that are randomly generated or computed from randomly generated values), do not set `reuse_previous_results` to `True`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Redispatch id: 52230138-e415-44c8-bd4c-f4986beb0601\n",
      "Redispatched workflow execution status: COMPLETED\n",
      "Redispatched workflow output: 6\n"
     ]
    }
   ],
   "source": [
    "redispatch_id = ct.redispatch(\n",
    "    dispatch_id, \n",
    "    replace_electrons={\"task_2\": task_2_redefined}, \n",
    "    reuse_previous_results=True\n",
    ")()\n",
    "print(f\"Redispatch id: {redispatch_id}\")\n",
    "result = ct.get_result(redispatch_id, wait=True)\n",
    "print(f\"Redispatched workflow execution status: {result.status}\")\n",
    "print(f\"Redispatched workflow output: {result.result}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following code compares the start and end times times of the reused results to demonstrate that the tasks were not re-run."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "# If previous results were reused to compute res_0 and res_1, execution time of the tasks should be 0\n",
    "# and the following two statements should be `True`.\n",
    "\n",
    "print(result.get_node_result(0)[\"start_time\"] == result.get_node_result(0)[\"end_time\"])\n",
    "print(result.get_node_result(2)[\"start_time\"] == result.get_node_result(2)[\"end_time\"])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3. Using new input parameters\n",
    "\n",
    "To evaluate the workflow with new input parameters, pass a new set of parameters in the `ct.dispatch` command."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Redispatch id: 57ab8b01-e6ee-454e-bd45-9122d750df33\n",
      "Redispatched workflow execution status: COMPLETED\n",
      "Redispatched workflow output: 20\n"
     ]
    }
   ],
   "source": [
    "redispatch_id = ct.redispatch(\n",
    "    dispatch_id, \n",
    "    replace_electrons={\"task_2\": task_2_redefined}, \n",
    "    reuse_previous_results=True\n",
    ")(1, 4)  # Input parameters are a=1 (same as before), b=4 (different)\n",
    "\n",
    "print(f\"Redispatch id: {redispatch_id}\")\n",
    "result = ct.get_result(redispatch_id, wait=True)\n",
    "print(f\"Redispatched workflow execution status: {result.status}\")\n",
    "print(f\"Redispatched workflow output: {result.result}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With `reuse_previous_results` set to `True`, only results that depend on the new input parameters are recomputed. In this case `res_0` is reused; `res_1` is recomputed since one of its arguments, `b`, is changed.\n",
    "\n",
    "Note that `res_0` is reused based on a comparison of its arguments: Covalent doesn't just recompute the results because the arguments were resubmitted. Instead, it sees that the resubmitted argument `a` is identical and thus correctly concludes that there is no need to recompute `res_0`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "# res_0 was reused; res_1 was recomputed since the value of b changed.\n",
    "\n",
    "print(result.get_node_result(0)[\"start_time\"] == result.get_node_result(0)[\"end_time\"])\n",
    "print(result.get_node_result(2)[\"start_time\"] == result.get_node_result(2)[\"end_time\"])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.13"
  },
  "vscode": {
   "interpreter": {
    "hash": "678d961ff063469efd5d78aa3ae546f6863f1ce650ebb0207bc985343e10a81d"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
